package com.data.permission.interceptor;

import com.data.permission.annotation.DataAuth;
import com.data.permission.condition.IConditionData;
import com.data.permission.constrant.CommonConstrant;
import com.data.permission.constrant.SourceTypeEnum;
import com.data.permission.rule.DataRuleInfoComponent;
import com.data.permission.rule.dto.ConditionDetailDTO;
import com.data.permission.rule.dto.RuleConditionDTO;
import com.data.permission.rule.dto.RuleInfoDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

@Aspect
@Slf4j
@Component
public class AnnotaionSqlInterceptor {

    @Autowired
    private Map<String, IConditionData> conditionDataMap;

    @Autowired
    private DataRuleInfoComponent dataRuleInfoComponent;

    @Pointcut("@annotation(com.data.permission.annotation.DataAuth)")
    public void annotationChargeManagement() {
    }

    @Around("annotationChargeManagement()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        DataAuth dataAuth = resolveMethod(point).getAnnotation(DataAuth.class);
        Object[] args = point.getArgs();
        StringBuilder dataAuthSql = new StringBuilder(" ");
        if (dataAuth.openArgs()) {
            String ruleCode = dataAuth.ruleCode();
            if (StringUtils.isNotBlank(ruleCode)) {
                //此处构建数据权限SQL条件
                RuleInfoDTO ruleInfoDTO = dataRuleInfoComponent.getRuleInfoMap().get(ruleCode);
                List<RuleConditionDTO> conditionList = ruleInfoDTO.getConditionList();
                if (!conditionList.isEmpty()) {
                    for (RuleConditionDTO ruleConditionDTO : conditionList) {
                        dataAuthSql.append(getSqlByCondition(ruleConditionDTO, args));
                    }
                    log.info("规则条件为：{}", dataAuthSql);
                }
            }
        }
        CommonConstrant.DATA_AUTH_SQL.set(dataAuthSql.toString());
        Object result = point.proceed();
        CommonConstrant.DATA_AUTH_SQL.remove();
        return result;
    }

    /**
     * 根据条件获取sql语句
     *
     * @param ruleCondition
     * @return
     */
    private String getSqlByCondition(RuleConditionDTO ruleCondition, Object[] args) {
        StringBuilder newSql = new StringBuilder();
        String firstCondition = null;
        boolean conditionType = false;
        for (ConditionDetailDTO conditionDetailDTO : ruleCondition.getConditionDetailList()) {
            if (StringUtils.isBlank(firstCondition) && 0 == conditionDetailDTO.getConditionOrder()) {
                firstCondition = conditionDetailDTO.getConditionFlag();
                /*拼接字段与对比条件 + 结果值*/
                if(conditionDetailDTO.sourceType.equals("2")) {
                    //如果为条件类型，则需要忽略当前拼接参数的条件值
                    conditionType = true;
                    newSql.append(" ").append(getParamValue(conditionDetailDTO, args));
                } else {
                    newSql.append(" ").append(conditionDetailDTO.getConditionField()).append(" ").append(getCheckTypeCode(conditionDetailDTO.getCheckType().trim())).append(" ").append(getParamValue(conditionDetailDTO, args));
                }
            } else if (StringUtils.isNotBlank(firstCondition)) {
                if(conditionDetailDTO.sourceType.equals("2")) {
                    //如果为条件类型，则需要忽略当前拼接参数的条件值
                    conditionType = true;
                    newSql.append(" ").append(getParamValue(conditionDetailDTO, args));
                } else {
                    newSql.append(" ").append(conditionDetailDTO.getConditionFlag()).append(" ").append(conditionDetailDTO.getConditionField()).append(" ").append(getCheckTypeCode(conditionDetailDTO.getCheckType().trim())).append(" ").append(getParamValue(conditionDetailDTO, args));
                }
            } else {
                //第一条非主要规则条件
                throw new RuntimeException("数据权限配置错误，未配置主要条件明细,第一条明细：" + conditionDetailDTO.getConditionOrder());
            }
        }
        if(StringUtils.isBlank(newSql)) {
            return "";
        }
        if(conditionType) {
            return newSql.toString();
        }
        return firstCondition + " ( " + newSql.toString() + " ) ";
    }

    /**
     * 拼接对比条件
     *
     * @param checkType
     * @return
     */
    private String getCheckTypeCode(String checkType) {
        if (checkType.trim().equals(">")) {
            return " &gt; ";
        } else if (checkType.trim().equals(">=")) {
            return " &gt;= ";
        } else if (checkType.trim().equals("<")) {
            return " &lt; ";
        } else if (checkType.trim().equals("<=")) {
            return " &lt;= ";
        } else if (checkType.trim().equals("=")) {
            return " = ";
        } else if (checkType.trim().equals("!=")) {
            return " != ";
        } else if (checkType.trim().toUpperCase().equals("IN")) {
            return " IN ";
        } else if (checkType.trim().toUpperCase().equals("NOT IN")) {
            return " NOT IN ";
        } else if (checkType.trim().toUpperCase().equals("EXISTS")) {
            return " EXISTS ";
        } else if (checkType.trim().toUpperCase().equals("NOT EXISTS")) {
            return " NOT EXISTS ";
        } else if(checkType.trim().toUpperCase().equals("IS NOT")) {
            return " IS NOT ";
        }
        return checkType;
    }


    public String getParamValue(ConditionDetailDTO conditionDetailDTO, Object[] args) {
        StringBuilder newSql = new StringBuilder();
        if ( (conditionDetailDTO.getCheckType().toUpperCase().trim().equals("IN") ||
                conditionDetailDTO.getCheckType().toUpperCase().trim().equals("NOT IN") ||
                conditionDetailDTO.getCheckType().toUpperCase().trim().equals("EXISTS") ||
                conditionDetailDTO.getCheckType().toUpperCase().trim().equals("NOT EXISTS")) && !conditionDetailDTO.getSourceType().equals("2")  ) {
            /*四种类型需要拼接大括号，但如果为条件复用规则时则不拼接处理*/
            newSql.append(" (").append(getValue(conditionDetailDTO, args)).append(" )");
        } else {
            /*基本类型直接拼接参数即可*/
            newSql.append(getValue(conditionDetailDTO, args));
        }
        return newSql.toString();
    }

    public String getValue(ConditionDetailDTO conditionDetailDTO, Object[] args) {
        StringBuilder newSql = new StringBuilder();
        if (SourceTypeEnum.METHOD.getCode().equals(conditionDetailDTO.getSourceType())) {
            if (!conditionDataMap.containsKey(conditionDetailDTO.getDataSource())) {
                throw new RuntimeException("数据权限配置错误，未能获取到参数方法对象：" + conditionDetailDTO.getDataSource());
            }
            IConditionData iConditionData = conditionDataMap.get(conditionDetailDTO.getDataSource());
            Object value = iConditionData.getValue(args);
            if (value instanceof List) {
                List values = (List) value;
                for (Object o : values) {
                    if (StringUtils.isNotBlank(newSql)) {
                        newSql.append(",'").append(o).append("'");
                    } else {
                        newSql.append("'").append(o).append("'");
                    }
                }
            } else {
                newSql.append("'").append(value).append("'");
            }
        } else if (SourceTypeEnum.CONDITION.getCode().equals(conditionDetailDTO.getSourceType())) {
            if (!dataRuleInfoComponent.getConditionMap().containsKey(conditionDetailDTO.getDataSource())) {
                throw new RuntimeException("未能获取到数据权限规则:" + conditionDetailDTO.getDataSource());
            }
            RuleConditionDTO ruleConditionDTO = dataRuleInfoComponent.getConditionMap().get(conditionDetailDTO.getDataSource());
            newSql.append(getSqlByCondition(ruleConditionDTO, args));
        } else if (SourceTypeEnum.SQL.getCode().equals(conditionDetailDTO.getSourceType())) {
            if("IS NOT".equals(conditionDetailDTO.getCheckType().toUpperCase().trim())) {
                newSql.append(" ").append(conditionDetailDTO.getDataSource()).append(" ");
            } else {
                newSql.append("(").append(conditionDetailDTO.getDataSource()).append(")");
            }
        } else {
            throw new RuntimeException("数据权限配置错误，SourceType：" + conditionDetailDTO.getSourceType());
        }
        return newSql.toString();
    }

    public static Method resolveMethod(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Class<?> targetClass = joinPoint.getTarget().getClass();
        Method method = getDeclaredMethodFor(targetClass, signature.getName(), signature.getMethod().getParameterTypes());
        if (null != method) {
            return method;
        }
        throw new IllegalStateException("Cannot resolve target method: " + signature.getMethod().getName());
    }

    public static Method getDeclaredMethodFor(Class<?> clazz, String name, Class<?>... parameterTypes) {
        try {
            return clazz.getDeclaredMethod(name, parameterTypes);
        } catch (NoSuchMethodException e) {
            /*当前类没取到，则取父类的*/
            Class<?> superClass = clazz.getSuperclass();
            if (null != superClass) {
                return getDeclaredMethodFor(superClass, name, parameterTypes);
            }
        }
        return null;
    }
}